﻿using System;
using System.Collections;
using System.Collections.Generic;

namespace bianyi
{
    public class Interpreter
    {
        public string strfilename;
        Parser p;
        Runtime rt;
        List<Context> ctxChain = new List<Context>();

        public Interpreter(string filename)
        {
            this.strfilename = filename;
            p = new Parser(strfilename);
            rt = new Runtime();
        }

        public void execute()
        {
            p.parse(this.rt);
            this.ctxChain.Add(new Context());
            var stmts = rt.getStatements();
            foreach (var stmt in stmts)
            {
                // std::cout << stmt->astString() << "\n";
                stmt.interpret(rt, ctxChain);
            }
        }
        public Value callFunction(Runtime rt, Function f)
        {
            return null;
        }
        public static Value assignSwitch(Token opt, Value lhs,
                                     Value rhs)
        {
            switch (opt)
            {
                case Token.TK_ASSIGN:
                    return rhs;
                case Token.TK_PLUS_AGN:
                    return lhs + rhs;
                case Token.TK_MINUS_AGN:
                    return lhs - rhs;
                case Token.TK_TIMES_AGN:
                    return lhs * rhs;
                case Token.TK_DIV_AGN:
                    return lhs / rhs;
                case Token.TK_MOD_AGN:
                    return lhs % rhs;
                default:
                    Console.WriteLine("InteralError: unexpects branch reached");
                    return null;
                    break;
            }
        }


        public static void enterContext(List<Context> ctxChain)
        {
            var tempContext = new Context();
            ctxChain.Add(tempContext);
        }

        internal static Value calcUnaryExpr(Value lhs, Token opt, int line, int column)
        {
            switch (opt)
            {
                case Token.TK_MINUS:
                    switch (lhs.type)
                    {
                        case ValueType.Int:
                            return new Value() { type = ValueType.Int, data = -Convert.ToInt32(lhs.data) };
                        case ValueType.Double:
                            return new Value() { type = ValueType.Double, data = -Convert.ToDouble(lhs.data) };
                        default:
                            Console.WriteLine("TypeError: invalid operand type for operator " + "-(negative) at line %d, col %d\n",
                                line, column);
                            return null;
                    }

                case Token.TK_LOGNOT:
                    if (lhs.type == ValueType.Bool)
                    {
                        return new Value() { type = ValueType.Bool, data = !Convert.ToBoolean(lhs.data) };

                    }
                    else
                    {
                        Console.WriteLine("TypeError: invalid operand type for operator " +
                            "!(logical not) at line %d, col %d\n",
                            line, column);
                    }
                    break;
                case Token.TK_BITNOT:
                    if (lhs.type == ValueType.Int)
                    {
                        return new Value() { type = ValueType.Int, data = ~Convert.ToInt32(lhs.data) };
                    }
                    else
                    {
                        Console.WriteLine(
                            "TypeError: invalid operand type for operator " +
                            "~(bit not) at line %d, col %d\n",
                            line, column);
                    }
                    break;
            }

            return lhs;
        }

        internal static Value calcBinaryExpr(Value lhs, Token opt, Value rhs, int line, int column)
        {
            Value result = null;

            switch (opt)
            {
                case Token.TK_PLUS:
                    result = (lhs + rhs);
                    break;
                case Token.TK_MINUS:
                    result = (lhs - rhs);
                    break;
                case Token.TK_TIMES:
                    result = (lhs * rhs);
                    break;
                case Token.TK_DIV:
                    result = (lhs / rhs);
                    break;
                case Token.TK_MOD:
                    result = (lhs % rhs);
                    break;
                //case Token.TK_LOGAND:
                //    result = (lhs && rhs);
                //    break;
                //case Token.TK_LOGOR:
                //    result = (lhs || rhs);
                //    break;
                //case Token.TK_EQ:
                //    result = (lhs == rhs);
                //    break;
                //case Token.TK_NE:
                //    result = (lhs != rhs);
                //    break;
                case Token.TK_GT:
                    result = (lhs > rhs);
                    break;
                case Token.TK_GE:
                    result = (lhs >= rhs);
                    break;
                case Token.TK_LT:
                    result = (lhs < rhs);
                    break;
                case Token.TK_LE:
                    result = (lhs <= rhs);
                    break;
                case Token.TK_BITAND:
                    result = (lhs & rhs);
                    break;
                case Token.TK_BITOR:
                    result = (lhs | rhs);
                    break;
            }

            return result;
        }

        internal static void leaveContext(List<Context> ctxChain)
        {
            var tempContext = ctxChain[ctxChain.Count - 1];
            ctxChain.Remove(tempContext);
        }
    }
}